#pragma once

#include <iostream>
#include <cstdio>
#include <vector>


class test_base {
public:
    static std::vector<test_base *> test_instances;
public:
    test_base() {
        test_instances.emplace_back(this);
    }

public:
    virtual void test() = 0;

    virtual bool is_active() = 0;

    virtual const char *get_test_name() = 0;

    virtual const char *get_test_case() = 0;

public:
    static void run_all_test() {
        for (auto &ti : test_instances) {
            if (ti->is_active()) {
                try {
                    printf("\033[33m[TEST] %s::%s [BEGIN]\033[0m\n", ti->get_test_name(), ti->get_test_case());
                    ti->test();
                    printf("\033[32m[TEST] %s::%s [PASSED]\033[0m\n", ti->get_test_name(), ti->get_test_case());
                    printf("-------------------------------\n");
                }
                catch (const std::exception &e) {
                    printf("\033[1m\033[31m[TEST] %s::%s [FAILED]: %s\033[0m\n", ti->get_test_name(), ti->get_test_case(), e.what());
                }
            }
        }
    }
};

std::vector<test_base *> test_base::test_instances;


#define TEST(test_name, case_name, active) \
static_assert(sizeof(#test_name) > 0, "test name cannot be blank!"); \
static_assert(sizeof(#case_name) > 0, "test case cannot be blank!"); \
class test_name##_##case_name##_test : public test_base {              \
public: \
    void test() override;                  \
    bool is_active() override {             \
        return _active; \
    } \
    const char* get_test_name() override { \
        return _test_name; \
    } \
    const char* get_test_case() override { \
        return _case_name; \
    } \
public: \
    const char* _test_name = #test_name;   \
    const char* _case_name = #case_name;   \
    bool _active = active;                 \
};                                         \
[[maybe_unused]] test_name##_##case_name##_test test_name##_##case_name##_test_inst; \
void test_name##_##case_name##_test::test()


#define TEST_MAIN int main() { \
    printf("-------------------------------\n"); \
    test_base::run_all_test(); \
    return 0;                  \
}


template<typename T, typename ... Args>
inline void println(T&& first, Args&&... args)
{
    std::cout << first;
    if constexpr (sizeof...(args) > 0) {
        std::cout << " ";
        println(args...);
    }
    else {
        std::cout << std::endl;
    }
}


TEST_MAIN
